package com.library.management.entity;

import com.library.management.enums.BorrowStatus;
import jakarta.persistence.*;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
import org.hibernate.annotations.CreationTimestamp;
import org.hibernate.annotations.UpdateTimestamp;

import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.time.temporal.ChronoUnit;

/**
 * 借阅记录实体类
 * 记录用户借阅图书的详细信息
 *
 * 关联关系：
 * - 多对一：用户、图书
 * - 一对多：罚款记录
 *
 * 业务功能：
 * - 借阅流程管理
 * - 逾期计算
 * - 续借处理
 *
 * @author 开发者
 * @version 1.0
 * @since 2025-10-19
 */
@Entity
@Table(name = "borrow_records", indexes = {
        @Index(name = "idx_borrow_user_id", columnList = "user_id"),
        @Index(name = "idx_borrow_book_id", columnList = "book_id"),
        @Index(name = "idx_borrow_status", columnList = "status"),
        @Index(name = "idx_borrow_due_date", columnList = "due_date"),
        @Index(name = "idx_borrow_date", columnList = "borrow_date")
})
@Data
@EqualsAndHashCode(exclude = {"user", "book"})
@ToString(exclude = {"user", "book"})
public class BorrowRecord {

    // ========== 主键标识 ==========

    /**
     * 记录ID - 主键，自增长
     * 数据库字段：id (BIGINT, PRIMARY KEY, AUTO_INCREMENT)
     */
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    // ========== 关联关系 ==========

    /**
     * 用户 - 借阅者
     * 多对一关系，外键关联users表
     * 延迟加载提高性能
     */
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "user_id", nullable = false, foreignKey = @ForeignKey(name = "fk_borrow_user_id"))
    private User user;

    /**
     * 图书 - 被借阅的图书
     * 多对一关系，外键关联books表
     * 延迟加载提高性能
     */
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "book_id", nullable = false, foreignKey = @ForeignKey(name = "fk_borrow_book_id"))
    private Book book;

    // ========== 借阅时间信息 ==========

    /**
     * 借书日期 - 借阅开始时间
     * 数据库字段：borrow_date (DATETIME), NOT NULL
     */
    @Column(name = "borrow_date", nullable = false)
    private LocalDateTime borrowDate;

    /**
     * 应还日期 - 预计归还时间
     * 数据库字段：due_date (DATETIME), NOT NULL
     */
    @Column(name = "due_date", nullable = false)
    private LocalDateTime dueDate;

    /**
     * 实际归还日期 - 实际归还时间
     * 数据库字段：actual_return_date (DATETIME)
     */
    @Column(name = "actual_return_date")
    private LocalDateTime actualReturnDate;

    // ========== 借阅状态信息 ==========

    /**
     * 借阅状态 - 当前借阅状态
     * BORROWED: 借阅中，RETURNED: 已归还，OVERDUE: 已逾期，RENEWED: 已续借，LOST: 已遗失
     * 数据库字段：status (ENUM), DEFAULT 'BORROWED'
     */
    @Enumerated(EnumType.STRING)
    @Column(nullable = false, length = 20)
    private BorrowStatus status = BorrowStatus.BORROWED;

    /**
     * 续借次数 - 已续借的次数
     * 数据库字段：renew_times (INT), DEFAULT 0
     */
    @Column(name = "renew_times")
    private Integer renewTimes = 0;

    // ========== 逾期罚款信息 ==========

    /**
     * 逾期天数 - 超过应还日期的天数
     * 数据库字段：overdue_days (INT), DEFAULT 0
     */
    @Column(name = "overdue_days")
    private Integer overdueDays = 0;

    /**
     * 逾期罚款 - 逾期产生的罚款金额
     * 数据库字段：overdue_fine (DECIMAL(10,2)), DEFAULT 0.00
     */
    @Column(name = "overdue_fine")
    private BigDecimal overdueFine = BigDecimal.ZERO;

    /**
     * 罚款是否已支付 - 标记罚款支付状态
     * 数据库字段：fine_paid (TINYINT(1)), DEFAULT 0
     */
    @Column(name = "fine_paid")
    private Boolean finePaid = false;

    // ========== 审计字段 ==========

    /**
     * 创建人ID - 记录创建者
     * 数据库字段：created_by (BIGINT)
     */
    @Column(name = "created_by")
    private Long createdBy;

    /**
     * 更新人ID - 记录最后修改者
     * 数据库字段：updated_by (BIGINT)
     */
    @Column(name = "updated_by")
    private Long updatedBy;

    /**
     * 创建时间 - 记录创建时间，自动生成
     * 数据库字段：created_time (DATETIME), DEFAULT CURRENT_TIMESTAMP
     */
    @CreationTimestamp
    @Column(name = "created_time", updatable = false)
    private LocalDateTime createdTime;

    /**
     * 更新时间 - 记录最后修改时间，自动更新
     * 数据库字段：updated_time (DATETIME), DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
     */
    @UpdateTimestamp
    @Column(name = "updated_time")
    private LocalDateTime updatedTime;

    // ========== 业务方法 ==========

    /**
     * 检查借阅是否已逾期
     * 比较当前时间与应还日期
     *
     * @return true-已逾期，false-未逾期
     */
    public boolean isOverdue() {
        return LocalDateTime.now().isAfter(this.dueDate) &&
                this.status != BorrowStatus.RETURNED;
    }

    /**
     * 计算逾期天数
     * 从应还日期到当前时间或实际归还时间的天数差
     *
     * @return 逾期天数
     */
    public int calculateOverdueDays() {
        if (this.actualReturnDate != null) {
            // 已归还，计算从应还日期到实际归还日期的天数
            return (int) ChronoUnit.DAYS.between(this.dueDate, this.actualReturnDate);
        } else if (isOverdue()) {
            // 未归还且已逾期，计算从应还日期到当前时间的天数
            return (int) ChronoUnit.DAYS.between(this.dueDate, LocalDateTime.now());
        } else {
            // 未逾期
            return 0;
        }
    }

    /**
     * 检查是否可以续借
     * 条件：未逾期、续借次数未达上限、未遗失
     *
     * @param maxRenewTimes 最大续借次数
     * @return true-可以续借，false-不可续借
     */
    public boolean canRenew(int maxRenewTimes) {
        return !isOverdue() &&
                this.renewTimes < maxRenewTimes &&
                this.status != BorrowStatus.LOST;
    }

    /**
     * 续借操作
     * 延长应还日期，增加续借次数，更新状态
     *
     * @param renewDays 续借天数
     * @param maxRenewTimes 最大续借次数
     * @return true-续借成功，false-续借失败
     */
    public boolean renew(int renewDays, int maxRenewTimes) {
        if (canRenew(maxRenewTimes)) {
            this.dueDate = this.dueDate.plusDays(renewDays);
            this.renewTimes++;
            this.status = BorrowStatus.RENEWED;
            return true;
        }
        return false;
    }

    /**
     * 归还图书
     * 更新实际归还日期和状态，计算逾期天数和罚款
     *
     * @param fineRate 逾期罚款率（元/天）
     * @return true-归还成功，false-归还失败
     */
    public boolean returnBook(BigDecimal fineRate) {
        if (this.status == BorrowStatus.RETURNED) {
            return false; // 已经归还过了
        }

        this.actualReturnDate = LocalDateTime.now();
        this.status = BorrowStatus.RETURNED;

        // 计算逾期天数和罚款
        this.overdueDays = calculateOverdueDays();
        if (this.overdueDays > 0) {
            this.overdueFine = fineRate.multiply(BigDecimal.valueOf(this.overdueDays));
        }

        return true;
    }

    /**
     * 获取借阅时长（天）
     * 从借书日期到实际归还日期或当前时间的天数
     *
     * @return 借阅时长（天）
     */
    public long getBorrowDuration() {
        LocalDateTime endDate = this.actualReturnDate != null ?
                this.actualReturnDate : LocalDateTime.now();
        return ChronoUnit.DAYS.between(this.borrowDate, endDate);
    }

    /**
     * 检查是否即将到期（在指定天数内到期）
     *
     * @param days 提前提醒天数
     * @return true-即将到期，false-未即将到期
     */
    public boolean isDueSoon(int days) {
        LocalDateTime now = LocalDateTime.now();
        LocalDateTime warningDate = this.dueDate.minusDays(days);
        return !now.isBefore(warningDate) && now.isBefore(this.dueDate) &&
                this.status == BorrowStatus.BORROWED;
    }
}